//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:    FAL.CPP

Abstract:       FLASH Abstraction Layer (FAL) for Windows CE 

-----------------------------------------------------------------------------*/
#include <windows.h>
#include <windev.h>
#include "storemgr.h"

#include "fal.h"

BOOL  FMDHOOK_ReadCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff);
int FMDHOOK_GetLastReadErrCode(SECTOR_ADDR errSectorAddr);
BOOL  FMDHOOK_WriteCapSector (DWORD dwBlockID, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff);
DWORD FMDHOOKInternal_GetBlockStatus(BLOCK_ID blockID, SectorInfo* pSI);

Fal::Fal (DWORD dwStartLogSector, DWORD dwStartPhysSector, BOOL fReadOnly) :
    m_dwStartLogSector(dwStartLogSector),
    m_dwStartPhysSector(dwStartPhysSector),
    m_dwNumLogSectors(0),
    m_fReadOnly(fReadOnly)
{
    m_dwLastAccessTick=GetTickCount();
    m_pMap = new MappingTable(dwStartLogSector, dwStartPhysSector);
    m_pSectorMgr = new SectorMgr();    
}

Fal::~Fal()
{
    delete m_pMap;
    delete m_pSectorMgr;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       FormatMedia()

Description:    Formats the FLASH by erasing all of the physical blocks
                on the media.

Notes:          The logical --> physical mapper and sector manager
                are automatically re-initialized.

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL Fal::FormatRegion(VOID)
{
    DWORD i = 0;

    if (m_fReadOnly) 
    {
        SetLastError(ERROR_WRITE_PROTECT);
        return FALSE;
    }

    //----- 3. Erase all of the valid blocks on the FLASH media -----
    //         NOTE: The file system is responsible for then 
    //               "logically" initializing the media as needed
    for(i = m_pRegion->dwStartPhysBlock; i < m_pRegion->dwStartPhysBlock + m_pRegion->dwNumPhysBlocks; i++)
    {    
        if(FMD.pGetBlockStatus((BLOCK_ID)i) & (BLOCK_STATUS_BAD | BLOCK_STATUS_RESERVED))                     
        {
            // Don't erase bad blocks on the media or blocks reserved for other use (such as bootloader).
            continue;
        }
            
        if(!FMD.pEraseBlock((BLOCK_ID)i))
        {
            ReportError((TEXT("FLASHDRV.DLL:FormatMedia() - FMD_EraseBlock(%d) failed\r\n"), i));
                
            if(!FMD.pSetBlockStatus((BLOCK_ID)i, BLOCK_STATUS_BAD))
            {
                ReportError((TEXT("FLASHDRV.FormatMedia() - FMD_MarkBlockBad(%d) failed\r\n"), i));
            }
            m_pSectorMgr->MarkBlockUnusable(i);
        }
    }
    return TRUE;
}



/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       InternalReadFromMedia()

Description:    Performs the specified scatter/gather READ request from the media.

Notes:          After parsing the parameters, the actual READ request is handled
                by the FLASH Media Driver (FMD).

Returns:        Boolean indicating success.
-------------------------------------------------------------------------------*/
BOOL Fal::ReadFromMedia(PSG_REQ pSG_req, BOOL fDoMap)
{
    static PSG_BUF      pSGbuff             = NULL;
    static PUCHAR       pBuff               = NULL;

    static DWORD        dwSGBuffNum          = 0;
    static DWORD        dwSGBuffPos          = 0;
    static DWORD        dwSectorAddr        = 0;

    static SECTOR_ADDR  physicalSectorAddr  = 0;

    SectExInfo sectExInfo;

    CELOG_ReadSectors(pSG_req->sr_start, pSG_req->sr_num_sec);

    //----- 1. Parse the scatter/gather request to determine if it can be fulfilled -----
    pSG_req->sr_status = ERROR_IO_PENDING;

    //----- 2. Now service the sector READ request(s) -----
    //         NOTE: The FLASH Media Driver (FMD) is invoked to read the data off the media 
    for(dwSGBuffNum=0; dwSGBuffNum<pSG_req->sr_num_sg; dwSGBuffNum++)
    {
        dwSGBuffPos  = 0;

        // Get a pointer to the scatter/gather buffer
        pSGbuff = &(pSG_req->sr_sglist[dwSGBuffNum]);

        if (fDoMap) 
        {    
            //Edit by duke;2007-09-03
            pBuff = (LPBYTE)MapCallerPtr(pSGbuff->sb_buf, pSGbuff->sb_len);
            if(NULL == pBuff)
            {
                RETAILMSG(1,(L"call MapPtrToProcess failed!!!!!\r\n"));
                goto READ_ERROR;
            }
        } 
        else 
        {
            pBuff = pSGbuff->sb_buf;
        }    

        for(dwSectorAddr=pSG_req->sr_start; 
            dwSectorAddr<(pSG_req->sr_start + pSG_req->sr_num_sec) && ((pSGbuff->sb_len - dwSGBuffPos) >= g_pFlashMediaInfo->dwDataBytesPerSector);
            dwSectorAddr++)
        {
            //----- 3. Perform the LOGICAL -> PHYSICAL mapping to determine the physical sector address. -----
            if( (!m_pMap->GetPhysicalSectorAddr((SECTOR_ADDR)dwSectorAddr, &physicalSectorAddr))
				|| (physicalSectorAddr == UNMAPPED_LOGICAL_SECTOR))
            {
                RETAILMSG(1, (TEXT("FLASHDRV.DLL:ReadFromMedia() - Unable to determine physical sector address for logical sector 0x%08x, physicalSectorAddr:0x%x\r\n"), dwSectorAddr, physicalSectorAddr));

                // RARE CONDITION: The file system has just asked us to read a sector that it has NEVER written
                //                 to;  in this situation, we don't read any data from the media and can just
                //                 return SUCCESS.
                pSG_req->sr_status = ERROR_SUCCESS;
                return TRUE;
            }

            // physicalSectorAddr is sectorexinfo
            if(!IS_VALID_PHY_SECT(physicalSectorAddr))
            {
                sectExInfo.dw=physicalSectorAddr;

                // deleted sector
                if(0==sectExInfo.del)
                {
                    RETAILMSG(1, (TEXT("FLASHDRV.DLL:ReadFromMedia() - read deleted sector logical sector 0x%08x, physicalSectorAddr:0x%x\r\n"), dwSectorAddr, physicalSectorAddr));
                    pSG_req->sr_status = ERROR_SUCCESS;
                    return TRUE;
                }

                // read error sector
                if(0==sectExInfo.rdErr)
                {
                    RETAILMSG(1, (TEXT("ReadFromMedia read sector:0x%x alread read bad\r\n"), dwSectorAddr));
                    pSG_req->sr_status = ERROR_READ_FAULT;
                    goto READ_ERROR;
                }
            }


            //----- For debugging purposes, print out the logical --> physical sector mapping... -----
            DEBUGMSG(ZONE_READ_OPS, (TEXT("FLASHDRV.DLL:ReadFromMedia() - logicalSector 0x%08x --> physicalSector 0x%08x\r\n"), dwSectorAddr, physicalSectorAddr));

            //----- 4. Invoke the FLASH Media Driver (FMD) to read the sector data from the media -----
            //         NOTE: Currently, only one sector is read at a time (last parameter)
            SectorMappingInfo mi;
            if(!FMD.pReadSector(physicalSectorAddr, (pBuff+dwSGBuffPos), (SectorInfo*)&mi, 1))  
            {   
                RETAILMSG(1,(TEXT("ReadSector logical:0x%x, physical:0x%x faild!!!\r\n"), dwSectorAddr, physicalSectorAddr));
                HandleReadFailure(physicalSectorAddr);
                
                pSG_req->sr_status = ERROR_READ_FAULT;
                goto READ_ERROR;
            }
            DEBUGMSG(ZONE_READ_OPS,(TEXT("ReadSector logical:0x%x, physical:0x%x\r\n"), dwSectorAddr, physicalSectorAddr));

            // this is a read error sector!
            if(0==mi.exInfo.rdErr)
            {
                RETAILMSG(1, (TEXT("ReadFromMedia read sector:0x%x alread read bad\r\n"), dwSectorAddr));
                pSG_req->sr_status = ERROR_READ_FAULT;
                goto READ_ERROR;
            }

            dwSGBuffPos  += g_pFlashMediaInfo->dwDataBytesPerSector;
        }

        pBuff = NULL;                    
    }

    
    m_dwLastAccessTick=GetTickCount();
    pSG_req->sr_status = ERROR_SUCCESS;
    return TRUE;

READ_ERROR:
    m_dwLastAccessTick=GetTickCount();
    return FALSE;
}

BOOL Fal::WriteToMedia(PSG_REQ pSG_req, BOOL fDoMap)
{
    static PSG_BUF      pSGbuff                     = NULL;
    static PUCHAR       pBuff                       = NULL;

    static DWORD        dwSGBuffNum                  = 0;
    static DWORD        dwSGBuffPos                  = 0;
    static DWORD        dwSectorAddr                = 0;

    static SECTOR_ADDR  physicalSectorAddr          = 0;
    static SECTOR_ADDR  existingPhysicalSectorAddr  = 0;

    static SectorMappingInfo sectorMappingInfo;

    CELOG_WriteSectors(pSG_req->sr_start, pSG_req->sr_num_sec);

    DWORD dwCurSector = pSG_req->sr_start;

    if (m_fReadOnly) 
    {
        pSG_req->sr_status = ERROR_WRITE_PROTECT;
        return FALSE;
    }

    //----- 1. Service the sector WRITE request(s) -----
    //         NOTE: The FLASH Media Driver (FMD) is invoked to write the data to the media 
    for(dwSGBuffNum=0; 
        dwSGBuffNum<pSG_req->sr_num_sg;
        dwSGBuffNum++)
    {
        dwSGBuffPos  = 0;

        // Get a pointer to the scatter/gather buffer
        pSGbuff = &(pSG_req->sr_sglist[dwSGBuffNum]);

        if (fDoMap) 
        {    
            //Edit by duke;2007-09-03
            pBuff = (LPBYTE)MapCallerPtr(pSGbuff->sb_buf, pSGbuff->sb_len);
            if(NULL == pBuff)
            {
                RETAILMSG(1,(L"call MapPtrToProcess failed!!!!!\r\n"));
                return FALSE;
            }
        } 
        else 
        {
            pBuff = pSGbuff->sb_buf;
        }           

        DWORD dwNumSectors = pSGbuff->sb_len / g_pFlashMediaInfo->dwDataBytesPerSector;
        DWORD dwError = InternalWriteToMedia (dwCurSector, dwNumSectors, pBuff);
        m_dwLastAccessTick=GetTickCount();

        pBuff = NULL;                            

        if (dwError != ERROR_SUCCESS)
        {
            ReportError((TEXT("FLASHDRV.DLL:WriteToMedia() - InternalWriteToMedia() failed.\r\n")));
            pSG_req->sr_status = dwError;
            return FALSE;
        }
        dwCurSector += dwNumSectors;

    }

    pSG_req->sr_status = ERROR_SUCCESS;
    return TRUE;
}

    
BOOL Fal::IsInRange (DWORD dwStartSector, DWORD dwNumSectors)
{
    return (dwStartSector >= m_dwStartLogSector && 
           ((dwStartSector + dwNumSectors) <= (m_dwStartLogSector + m_dwNumLogSectors)));
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       StartupFAL()

Description:    Starts up the FLASH Abstraction Layer (FAL)

Notes:          The FLASH Media Driver (FMD) needs to be completely
                initialized before this function is called.  If 
                startup fails, the caller is responsible for "cleaning 
                things up" by calling ShutdownFAL().

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL Fal::StartupFAL(PFlashRegion pRegion)
{   

    //----- 1. Cache size properties of the FLASH media -----
    m_dwSectorsPerBlock = pRegion->dwSectorsPerBlock;
    m_pRegion = pRegion;
    m_dwNumLogSectors = pRegion->dwNumLogicalBlocks * m_dwSectorsPerBlock;            

    //----- 2. Initialize the LOGICAL -> PHYSICAL sector mapper -----
    if (!m_pMap->Init (pRegion))
    {
        ReportError((TEXT("FLASHDRV.DLL:StartupFAL() - Unable to initialize Logical --> Physical Mapper\r\n")));
        return FALSE;
    }

    //----- 3. Build the logical --> physical lookup table, free list, etc. -----
    if(!BuildupMappingInfo())
    {
        ReportError((TEXT("FLASHDRV.DLL:StartupFAL() - Unable to build the logical --> physical lookup table, free list, etc.!!!\r\n")));
        return FALSE;
    }

    return TRUE;
}



/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       ShutdownFAL()

Description:    Shuts down the FLASH Abstraction Layer (FAL)

Notes:          The logical --> physical mapper and sector manager
                are appropriately deinitialized.  Note that the caller
                is responsible for deinitializing the FMD.

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL Fal::ShutdownFAL()
{
    //----- 1. Deinitialize the LOGICAL -> PHYSICAL sector mapper -----
    if (!m_pMap->Deinit())
    {
        ReportError((TEXT("FLASHDRV.DLL:ShutdownFAL() - Unable to deinitialize Logical --> Physical Mapper\r\n")));
    }

    //----- 2. Deinitialize the sector manager -----
    if (!m_pSectorMgr->Deinit())
    {
        ReportError((TEXT("FLASHDRV.DLL:ShutdownFAL() - Unable to deinitialize Sector Manager\r\n")));
    }
    
    return TRUE;    
}


BOOL XipFal::StartupFAL(PFlashRegion pRegion)
{   

    if (!m_pSectorMgr->Init (pRegion, this, NULL))
    {
        ReportError((TEXT("FLASHDRV.DLL:StartupFAL() - Unable to initialize Sector Manager\r\n")));
        return FALSE;
    }

    return Fal::StartupFAL(pRegion);
}


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       BuildupMappingInfo()

Description:    Reads the sector info for each sector on the media
                and builds up the MAPPING, FREE, and DIRTY sector info.

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL XipFal::BuildupMappingInfo()
{
    DWORD dwBlockID = 0;
    DWORD dwPhysSector = m_dwStartPhysSector;
    DWORD dwLogSector = m_dwStartLogSector;
    DWORD dwExistingPhysSector;

    DEBUGMSG(1, (TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Enter. \r\n"), dwBlockID));

    for (dwBlockID = m_pRegion->dwStartPhysBlock; dwBlockID < m_pRegion->dwStartPhysBlock + m_pRegion->dwNumPhysBlocks; dwBlockID++)
    {
        DWORD iSector;
        DWORD dwStatus = FMD.pGetBlockStatus (dwBlockID);

        if (dwStatus & (BLOCK_STATUS_RESERVED | BLOCK_STATUS_BAD))
        {
            if(!m_pSectorMgr->MarkBlockUnusable(dwBlockID))
            {
                ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - SectorMgr::MarkBlockUnusable(%d) failed. \r\n"), dwBlockID));
            }                    
            dwPhysSector += m_pRegion->dwSectorsPerBlock;
            continue;
        }
        else if (dwStatus & BLOCK_STATUS_READONLY)
        {                    
            if(!m_pSectorMgr->AddSectorsToList (SectorMgr::ReadOnly, dwLogSector, m_pRegion->dwSectorsPerBlock))
            {
                ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to mark sector %x as read only.\r\n"), dwPhysSector));
            }
        }
        else
        {
            if(!m_pSectorMgr->AddSectorsToList (SectorMgr::XIP, dwLogSector, m_pRegion->dwSectorsPerBlock))
            {
                ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to mark sector %x as XIP.\r\n"), dwPhysSector));
            }
        }
        

        // Map logical sectors 1:1 with physical sectors
        for (iSector = 0; iSector < m_pRegion->dwSectorsPerBlock; iSector++)
        {
            if(!m_pMap->MapLogicalSector(dwLogSector + iSector, dwPhysSector + iSector, &dwExistingPhysSector))
            {
                ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to map logical sector 0x%08x to physical sector 0x%08x\r\n"), 
                                            dwLogSector + iSector, dwPhysSector + iSector));
            }
        }

        dwLogSector += m_pRegion->dwSectorsPerBlock;
        dwPhysSector += m_pRegion->dwSectorsPerBlock;            
    }

    return TRUE;
}


DWORD XipFal::InternalWriteToMedia(DWORD dwStartSector, DWORD dwNumSectors, LPBYTE pBuffer)
{
    LPBYTE pbBlock = NULL;
    DWORD dwError = ERROR_GEN_FAILURE;
    DWORD iSector;

    DWORD dwNumSectorsLeft = dwNumSectors;
    DWORD dwCurSector = dwStartSector;

    while (dwNumSectorsLeft)
    {
        DWORD dwPhysSector, dwPhysBlock, dwBlockOffset;
        LPBYTE pTmpBuffer = pBuffer;
        DWORD dwSectorsToWrite = m_pRegion->dwSectorsPerBlock;

        if (!m_pMap->GetPhysicalSectorAddr (dwCurSector, &dwPhysSector))
        {
            ReportError((TEXT("FLASHDRV.DLL:WriteXIPSectors() - Could not get physical sector for logical sector 0x%x.\r\n"), dwCurSector));
            dwError = ERROR_INVALID_PARAMETER;
            goto XIP_ERROR;
        }
        
        dwPhysBlock = dwPhysSector / m_pRegion->dwSectorsPerBlock;
        dwBlockOffset = dwPhysSector % m_pRegion->dwSectorsPerBlock;
    
        if (dwBlockOffset || (dwNumSectorsLeft < m_pRegion->dwSectorsPerBlock))
        {
            // If the write does not contain the entire block, read in the block, 
            // apply to partial change to the buffer, erase the block, and write
            // it back out.

            if (!pbBlock)
            {
                pbBlock = (LPBYTE)LocalAlloc (LMEM_FIXED, m_pRegion->dwBytesPerBlock);
                if (!pbBlock)
                {
                    dwError = ERROR_OUTOFMEMORY;
                    goto XIP_ERROR;
                }
            }

            pTmpBuffer = pbBlock;

            //
            // Read the block in
            // 
            
            for (iSector = 0; iSector < m_pRegion->dwSectorsPerBlock; iSector++)
            {
                if (!FMD.pReadSector(dwPhysBlock * m_pRegion->dwSectorsPerBlock + iSector, pTmpBuffer, NULL, 1))
                {                    
                    ReportError((TEXT("FLASHDRV.DLL:WriteXIPSectors() - Read sector failed for 0x%x.\r\n"), dwPhysBlock * m_pRegion->dwSectorsPerBlock + iSector));
                    goto XIP_ERROR;
                }
                pTmpBuffer += g_pFlashMediaInfo->dwDataBytesPerSector;
            }
          
            dwSectorsToWrite = MIN (dwNumSectorsLeft, m_pRegion->dwSectorsPerBlock - dwBlockOffset);                      

            memcpy (pbBlock + dwBlockOffset * g_pFlashMediaInfo->dwDataBytesPerSector, pBuffer, dwSectorsToWrite * g_pFlashMediaInfo->dwDataBytesPerSector);

            pTmpBuffer = pbBlock;
            
        }

        if (!FMD.pEraseBlock (dwPhysBlock))
        {
            ReportError((TEXT("FLASHDRV.DLL:WriteXIPSectors() - Erase block failed for 0x%x.\r\n"), dwPhysBlock));
            goto XIP_ERROR;
        }

        //
        // Write the entire block out
        // 
        
        for (iSector = 0; iSector < m_pRegion->dwSectorsPerBlock; iSector++)
        {
            if (!FMD.pWriteSector(dwPhysBlock * m_pRegion->dwSectorsPerBlock + iSector, pTmpBuffer, NULL, 1))
            {                    
                ReportError((TEXT("FLASHDRV.DLL:WriteXIPSectors() - Write sector failed for 0x%x.\r\n"), dwPhysBlock * m_pRegion->dwSectorsPerBlock + iSector));
                goto XIP_ERROR;
            }
            pTmpBuffer += g_pFlashMediaInfo->dwDataBytesPerSector;
        }

        pBuffer += dwSectorsToWrite * g_pFlashMediaInfo->dwDataBytesPerSector;
        dwCurSector += dwSectorsToWrite;
        dwNumSectorsLeft -= dwSectorsToWrite;

        DEBUGMSG(1, (TEXT("%d\n"), dwPhysBlock));
    }    

    dwError = ERROR_SUCCESS;

XIP_ERROR:

    if (pbBlock)
        LocalFree (pbBlock);
    
    return dwError;
    
}

BOOL XipFal::DeleteSectors(DWORD dwStartLogSector, DWORD dwNumSectors)
{
    return FALSE;
}

BOOL XipFal::SecureWipe()
{
    return FALSE;
}

DWORD XipFal::SetSecureWipeFlag(DWORD dwStartBlock)
{
    return INVALID_BLOCK_ID;
}
 
FileSysFal::FileSysFal (DWORD dwStartLogSector, DWORD dwStartPhysSector, BOOL fReadOnly) : 
    Fal(dwStartLogSector, dwStartPhysSector, fReadOnly) 
{
    m_pCompactor = new Compactor();
    m_pWtCnt=NULL;
	m_iMaxWtCnt=INIT_WRITE_CNT;
}

FileSysFal::~FileSysFal()
{
	if(NULL!=m_pWtCnt)
	{
		delete m_pWtCnt;
	}
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       StartupFAL()

Description:    Starts up the FLASH Abstraction Layer (FAL)

Notes:          The FLASH Media Driver (FMD) needs to be completely
                initialized before this function is called.  If 
                startup fails, the caller is responsible for "cleaning 
                things up" by calling ShutdownFAL().

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL FileSysFal::StartupFAL(PFlashRegion pRegion)
{   

    //----- 3. Initialize the sector manager -----
    if (!m_pSectorMgr->Init (pRegion, this, m_pCompactor))
    {
        ReportError((TEXT("FLASHDRV.DLL:StartupFAL() - Unable to initialize Sector Manager\r\n")));
        return FALSE;
    }

    //----- 5. Initialize and start the compactor -----
    if(!m_pCompactor->Init (pRegion, this))
    {
        ReportError((TEXT("FLASHDRV.DLL:StartupFAL() - Unable to initialize the compactor!!!\r\n")));
        return FALSE;
    }   

    // use startup write cache for startup
	if(NULL!=m_pWtCnt)
	{
		delete m_pWtCnt;
	}
	m_pWtCnt=new WtCntCachStartUp(m_dwStartLogSector,  pRegion->dwNumLogicalBlocks* pRegion->dwSectorsPerBlock);    // m_dwNumLogSectors is usable only after Fal::StartupFAL is called
	m_pWtCnt->InitWtCntCache();

	BOOL fRet=Fal::StartupFAL(pRegion);

	// use normal write cache
	delete m_pWtCnt;
	m_pWtCnt=new WtCntCachNormal(m_dwStartLogSector, m_dwNumLogSectors, m_dwSectorsPerBlock);
	if(FALSE==m_pWtCnt->InitWtCntCache())
	{
        return FALSE;
	}

	return fRet;
}



/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       ShutdownFAL()

Description:    Shuts down the FLASH Abstraction Layer (FAL)

Notes:          The logical --> physical mapper and sector manager
                are appropriately deinitialized.  Note that the caller
                is responsible for deinitializing the FMD.

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL FileSysFal::ShutdownFAL()
{
    if(!m_pCompactor->Deinit())
    {
        ReportError((TEXT("FLASHDRV.DLL:ShutdownFAL() - Unable to deinitialize the Compactor\r\n")));
    }
    
	if(NULL!=m_pWtCnt)
	{
		delete m_pWtCnt;
		m_pWtCnt=NULL;
	}

    return Fal::ShutdownFAL();    
}


BOOL FileSysFal::BuildupMappingInfo()
{
    DWORD i = 0;
    DWORD dwBlockID = 0;
    DWORD dwPhysSector = m_dwStartPhysSector;
    DWORD dwLogSector = m_dwStartLogSector;
    DWORD dwExistingPhysSector = 0;
    //used for capped SectorMappingInfo
    SectorMappingInfo*  psectorMappingInfo=(SectorMappingInfo*)LocalAlloc(LPTR, m_pRegion->dwBytesPerBlock/m_pRegion->dwSectorsPerBlock);
    if(!psectorMappingInfo)
    {
        return FALSE;
    }

    DWORD dwUncapBlockNumb=0;
    DWORD dwUncapBlocks[8];				// for diskimage generate img, it will has more uncaped blocks

#define MAX_READ_ERR_BLOCK  32
    DWORD dwReadErrBlocks[MAX_READ_ERR_BLOCK];
    DWORD dwCurErrBlockId=0;

    SectExInfo sectExInfo;

    DEBUGMSG(ZONE_BLDMAP, (TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Enter. \r\n")));

    //----- 1. Read the entire media to determine the status of all sectors -----
    for (dwBlockID = m_pRegion->dwStartPhysBlock; dwBlockID < m_pRegion->dwStartPhysBlock + m_pRegion->dwNumPhysBlocks; dwBlockID++)
    {

		// get all spareinfo of the block
		SectorInfo si[2];
        DWORD dwStatus=0;
        BOOL fUncaped=FALSE;
        // compact this block lator to try get infos
		if(!GetBlockSpareInfos(dwBlockID, (LPBYTE)psectorMappingInfo, &si[0], (~BLOCK_STATUS_READONLY), &dwStatus, &fUncaped))
		{
            // block read error but not mark special status!
            RETAILMSG(1, (TEXT("!!! GetBlockSpareInfos dwBlockID:0x%x faild try using wrong info!!!\r\n"), dwBlockID));
            if(dwCurErrBlockId==(MAX_READ_ERR_BLOCK))
            {
                ReportError((TEXT("!!! too many bad blocks, can't try again!!!\r\n")));
                goto INIT_ERROR;
            }
            dwReadErrBlocks[dwCurErrBlockId]=dwBlockID;
            dwCurErrBlockId++;

            // continue build, since add to bad array, this block will be compact lator
//            dwPhysSector += m_pRegion->dwSectorsPerBlock; // Move to first sector in next block
//			continue;
		}

        if( dwStatus 
            && (BLOCK_STATUS_READONLY!=dwStatus) )    //we only deal with BLOCK_STATUS_READONLY "only" status others mark as unsed
        {
            if(!m_pSectorMgr->MarkBlockUnusable(dwBlockID))
            {
                ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - SectorMgr::MarkBlockUnusable(%d) failed. \r\n"), dwBlockID));
            }                    
            dwPhysSector += m_pRegion->dwSectorsPerBlock; // Move to first sector in next block
            continue;
        }

        //
        // deal the uncapped and not readonly and not free block
        if( (fUncaped)
            && (dwReadErrBlocks[dwCurErrBlockId-1]!=dwBlockID) )
        {
            if(!(dwStatus & BLOCK_STATUS_READONLY))
            {
                if(FREE_SECTOR!=psectorMappingInfo[0].fDataStatus 
                   || UNMAPPED_LOGICAL_SECTOR!=psectorMappingInfo[0].logicalSectorAddr)
                {
                    DEBUGMSG( ZONE_BLDMAP, (TEXT("dwBlockID:0x%x uncapped, and not free. \r\n"), dwBlockID) );
                    if(dwUncapBlockNumb>=(sizeof(dwUncapBlocks)/sizeof(dwUncapBlocks[0])))
                    {
                        RETAILMSG( 1, (TEXT("!!! ERROR too many uncaped blocks, can't continue, please format disk!!! \r\n"), dwBlockID) );
//FormatRegion();
                        goto INIT_ERROR;
                    }
                    dwUncapBlocks[dwUncapBlockNumb]=dwBlockID;
                    dwUncapBlockNumb++;
                    ASSERT(dwUncapBlockNumb<=2);
                }
            }
        }
        
        //----- 4. Read the sector information stored on the physical media for each sector -----
        for(i=0; i<(m_pRegion->dwSectorsPerBlock); i++, dwPhysSector++)
        {
            //----- Notice that SectorMappingInfo is cast to SectorInfo.  SectorInfo is the public -----
            //      definition of the private structure SectorMappingInfo.  Consequently, any 
            //      changes to the SectorMappingInfo structure will require the SectorInfo 
            //      structure's size be updated accordingly.

            // First sector in a block must be marked RO to indicate all sectors in the block are RO.
            if ((dwStatus & BLOCK_STATUS_READONLY) && (i == 0))
            {         
                DEBUGMSG( ZONE_BLDMAP, (TEXT("readonly block:0x%x, start logical sector:0x%x\r\n"), dwBlockID, psectorMappingInfo[i].logicalSectorAddr) );
                DWORD iSector;
                
                if(!m_pSectorMgr->AddSectorsToList(SectorMgr::ReadOnly, psectorMappingInfo[i].logicalSectorAddr, m_pRegion->dwSectorsPerBlock))
                {
                    ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to mark sector %x as read only.\r\n"), dwPhysSector));
                }
                
                if(IsSectorFree(psectorMappingInfo[i]))
                {
                    RETAILMSG( 1, (TEXT("readonly block:0x%x all free!!!\r\n"), dwBlockID) );
                    if(!m_pSectorMgr->MarkBlockUnusable(dwBlockID))
                    {
                        ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - SectorMgr::MarkBlockUnusable(%d) failed. \r\n"), dwBlockID));
                    }                    
                }
                else
                {
                    for (iSector = 0; iSector < m_pRegion->dwSectorsPerBlock; iSector++)
                    {
                        if(IsSectorFree(psectorMappingInfo[i]))
                        {
                            RETAILMSG( 1, (TEXT("readonly block:0x%x sector:0x%x free!!!\r\n"), dwBlockID, iSector) );
                            break;
                        }
                        if(!m_pMap->MapLogicalSector(psectorMappingInfo[i].logicalSectorAddr + iSector, dwPhysSector + iSector, &dwExistingPhysSector))
                        {
                            ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to map logical sector 0x%08x to physical sector 0x%08x\r\n"), 
                                                        psectorMappingInfo[i].logicalSectorAddr + iSector, dwPhysSector + iSector));
                        }
                        ChckChgMaxCnt(psectorMappingInfo[i].exInfo.iWtCnt);
                        CacheWtCnt(psectorMappingInfo[i].logicalSectorAddr + iSector, &psectorMappingInfo[i].exInfo);
                    
                        ASSERT(UNMAPPED_LOGICAL_SECTOR==dwExistingPhysSector);
                    }
                }
                
                dwPhysSector += m_pRegion->dwSectorsPerBlock;
                break;
            }

            if (IsSecureWipeInProgress(psectorMappingInfo[i]))
            {
                DEBUGMSG( ZONE_BLDMAP||ZONE_WARNING, (TEXT("wipe not complete last time, continue wipe.\r\n")) );
                SecureWipe();
                MarkSectorFree(psectorMappingInfo[i]);
            }


            //----- 5. Free sector?  If so, add it to the "free sector list" the Sector Manager uses -----
            //         NOTE: Notice that we ONLY mark this sector as FREE if it is in a valid block.
            if(IsSectorFree(psectorMappingInfo[i]))
            {
                // OPTIMIZATION: Because the compactor always adds whole blocks of FREE sectors to the
                //               Sector Manager, we know that if the first sector in a block is FREE then 
                //               all other sectors in this block are also FREE.  Consequently, when the first 
                //               sector in a block is FREE we don't have to read the other sectors in the block.
            
                DEBUGMSG( ZONE_BLDMAP, (TEXT("Free sector:0x%x, all remain:0x%x as free!\r\n"), dwPhysSector,  m_pRegion->dwSectorsPerBlock-i) );
                if(!m_pSectorMgr->AddSectorsToList(SectorMgr::Free, dwPhysSector, m_pRegion->dwSectorsPerBlock-i))
                {
                    ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to mark sector %x as free.\r\n"), dwPhysSector));
                }
                dwPhysSector+=(m_pRegion->dwSectorsPerBlock-i);
                break;
            }

            if( (IsSectorDirty(psectorMappingInfo[i]))
                || (!GetWtCnt(psectorMappingInfo[i].logicalSectorAddr, &sectExInfo) )
                || (sectExInfo.iWtCnt>psectorMappingInfo[i].exInfo.iWtCnt) )
            {
                if(!m_pSectorMgr->MarkSectorsAsDirty(dwPhysSector, 1))
                {
                    ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to mark sector %x as dirty.\r\n"), dwPhysSector));
                }

                continue;
            }

            //
            // now this sector is a "valid" sector
            if(!m_pMap->MapLogicalSector(psectorMappingInfo[i].logicalSectorAddr, dwPhysSector, &dwExistingPhysSector))
            {
                ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to map logical sector 0x%08x to physical sector 0x%08x\r\n"), 
                                            psectorMappingInfo[i].logicalSectorAddr, dwPhysSector));
            }

			// update cache
			ChckChgMaxCnt(psectorMappingInfo[i].exInfo.iWtCnt);
			CacheWtCnt(psectorMappingInfo[i].logicalSectorAddr, &psectorMappingInfo[i].exInfo);
                
            //
            // if there is existing sector, mark it as dirty
            if(UNMAPPED_LOGICAL_SECTOR!=dwExistingPhysSector)
            {
                if(!m_pSectorMgr->MarkSectorsAsDirty(dwExistingPhysSector, 1))
                {
                    ReportError((TEXT("FLASHDRV.DLL:BuildupMappingInfo() - Unable to mark sector %x as dirty.\r\n"), dwPhysSector));
                }
            }
        }
    }

    //
    // build mapping table ok, now do some check
    //

    if(0==m_pSectorMgr->GetNumberOfFreeSectors())
    {
        RETAILMSG( 1, (TEXT("no free sector at init error!!!\r\n")));
//- ken mount this disk even there is no free sectors
//        goto INIT_ERROR;
    }
    
    for(i=0; i<dwCurErrBlockId; i++)
    {
        RETAILMSG( 1, (TEXT("compact read err block:0x%x\r\n"), dwReadErrBlocks[i]) );
        m_pCompactor->CompactBlock(dwReadErrBlocks[i], USE_SECTOR_MAPPING_INFO, TRUE);
    }
    
    if(1<dwUncapBlockNumb)
    {
        RETAILMSG( 1, (TEXT("!!! too many uncaped blocks:%d !!!\r\n"), dwUncapBlockNumb) );
        for(i=0; i<dwUncapBlockNumb; i++)
        {
            RETAILMSG( 1, (TEXT("dwBlockID:0x%x uncapped, compact it. \r\n"), dwUncapBlocks[i]) );
            m_pCompactor->CompactBlock(dwUncapBlocks[i], USE_SECTOR_MAPPING_INFO);
        }
    }
    
    
    if(psectorMappingInfo)
    {
        LocalFree(psectorMappingInfo);
    }
    
    DumpMappingTable(ZONE_BLDMAP, m_pMap);
    DumpInfos();

    return TRUE;

INIT_ERROR:
    if(psectorMappingInfo)
    {
        LocalFree(psectorMappingInfo);
    }
    return FALSE;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       InternalWriteToMedia()

Description:    Performs the specified scatter/gather WRITE request to the media.

Notes:          After parsing the parameters, the actual WRITE request is handled
                by the FLASH Media Driver (FMD).

Returns:        Boolean indicating success.
-------------------------------------------------------------------------------*/
DWORD FileSysFal::InternalWriteToMedia(DWORD dwStartSector, DWORD dwNumSectors, LPBYTE pBuffer)
{
    DWORD        dwSGBuffNum                  = 0;
    DWORD        dwSGBuffPos                  = 0;
    DWORD        dwSectorAddr                = 0;

    SECTOR_ADDR  physicalSectorAddr          = 0;
    SECTOR_ADDR  existingPhysicalSectorAddr  = 0;

    SectorMappingInfo sectorMappingInfo;

    DWORD dwError = ERROR_GEN_FAILURE;


    for(dwSectorAddr = dwStartSector; dwSectorAddr < (dwStartSector + dwNumSectors); dwSectorAddr++) 
    {
        //----- 2. Write the sector data to disk.  Notice that this operation is repetitively -----
        //         tried ONLY if FMD_WriteSector() fails.  Unless the FLASH block we are writing to
        //         has just gone bad, this loop will only execute once.
        for(;;)
        {
            //----- 3. Get a free physical sector to store the data into.  If this call FAILS, the WRITE -----
            //         cannot succeed because the media is full.
            if(!m_pSectorMgr->GetNextFreeSector(&physicalSectorAddr, FALSE))
            {
                ReportError((TEXT("FLASHDRV.DLL:WriteToMedia() - Unable to get next free physical sector address for writing!  The media is full...\r\n")));
                dwError = ERROR_DISK_FULL;
                goto WRITE_ERROR;
            }
        
            //----- For debugging purposes, print out the logical --> physical mapping... -----
            DEBUGMSG(ZONE_WRITE_OPS, (TEXT("FLASHDRV.DLL:WriteToMedia() - logicalSector 0x%08x --> physicalSector 0x%08x\r\n"), dwSectorAddr, physicalSectorAddr));

            //----- 4. Start the write operation (used to safeguard against power failure conditions) -----
            //         The rationale is as follows: If we lose power during before the data is written
            //         to the media, this bit should be set and this will allow us to detect that a 
            //         WRITE was in progress when the power failed.
            memset(&sectorMappingInfo, 0xFF, sizeof(SectorMappingInfo));
            sectorMappingInfo.logicalSectorAddr = dwSectorAddr;
            MarkSectorWriteCompleted(sectorMappingInfo);

            //
            // get write count
            SectExInfo sectExInfo;
            if(GetWtCnt(dwSectorAddr, &sectExInfo, NULL))
            {
                sectorMappingInfo.exInfo.dw=0xffffffff;
                sectorMappingInfo.exInfo.iWtCnt=sectExInfo.iWtCnt+1;
				m_pWtCnt->CacheWtCnt(dwSectorAddr, &sectorMappingInfo.exInfo);
            }
            // can't get writecnt, so the read to this logical sector must faild
            else
            {
                SECTOR_ADDR dwPhysSector;
                if(m_pMap->GetPhysicalSectorAddr(dwSectorAddr, &dwPhysSector) )
                {
                    RETAILMSG(1,(TEXT("WriteSector logSectorAddr:0x%x can't get write count from physical:0x%x!!!\r\n"), dwSectorAddr, dwPhysSector));
                    HandleReadFailure(dwPhysSector);
                }

                goto WRITE_ERROR;
            }

            //
            // do real write
            if(!FMD.pWriteSector(physicalSectorAddr, (pBuffer+dwSGBuffPos), (PSectorInfo)&sectorMappingInfo, 1))  
            {   
                RETAILMSG(1,(TEXT("FLASHDRV.DLL:WriteToMedia() - Unable to write to physical sector 0x%08x!  Calling HandleWriteFailure()\r\n"), 
                                               physicalSectorAddr));
        
                // restore the cache info!
                m_pWtCnt->CacheWtCnt(dwSectorAddr, &sectExInfo);

                // WRITE operation failed, try to recover...
                if(!HandleWriteFailure(physicalSectorAddr))
                {
                    ReportError((TEXT("FLASHDRV.DLL:WriteToMedia() - Unable to handle the WRITE failure to sector 0x%08x\r\n"), physicalSectorAddr));
                    goto WRITE_ERROR;
                }           
                continue;                                           // Try the WRITE at another physical sector...
            }

            //----- 6. Perform the LOGICAL -> PHYSICAL mapping... -----
            if(!m_pMap->MapLogicalSector(dwSectorAddr, physicalSectorAddr, &existingPhysicalSectorAddr))
            {
                ReportError((TEXT("FLASHDRV.DLL:WriteToMedia() = FATAL_ERROR: Unable to map logical sector 0x%08x to physical sector 0x%08x\r\n"), 
                                            dwSectorAddr, physicalSectorAddr));
                goto WRITE_ERROR;
            }

			ChckChgMaxCnt(sectorMappingInfo.exInfo.iWtCnt);
			CacheWtCnt(dwSectorAddr, &sectorMappingInfo.exInfo);
            DEBUGMSG(ZONE_WRITE_OPS,(TEXT("WriteSector logical:0x%x, physical:0x%x, exist:0x%x, Write count:0x%x\r\n"), dwSectorAddr, physicalSectorAddr, existingPhysicalSectorAddr, sectorMappingInfo.exInfo.iWtCnt));


            //----- 7. If this logical sector was already mapped, mark the "stale data" in the existing -----
            //         physical sector as "dirty."
            if( IS_VALID_PHY_SECT(existingPhysicalSectorAddr) )
            {
                // Inform the Sector Manager that this sector is DIRTY...
                if(!m_pSectorMgr->MarkSectorsAsDirty(existingPhysicalSectorAddr, 1))
                {
                    ReportError((TEXT("FLASHDRV.DLL:WriteToMedia() - FATAL_ERROR: SM_MarkSectorsAsDirty(0x%08x) failed!\r\n"), existingPhysicalSectorAddr));
                    goto WRITE_ERROR;
                }                                       
            }

            //----- 8. At this point, the WRITE operation has completed successfully. -----
            break;
        }

        dwSGBuffPos  += g_pFlashMediaInfo->dwDataBytesPerSector;
    }

    return ERROR_SUCCESS;

WRITE_ERROR:
    DEBUGMSG(ZONE_WARNING,(TEXT("InternalWriteToMedia faild!!!\r\n")));
    return dwError;
}




/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       HandleWriteFailure()

Description:    Handles the case when a WRITE to the media fails.  If a WRITE operation 
                fails, this is an indication that this FLASH block is going bad.  
                Consequently, any remaining FREE sectors in this block should be removed 
                from the Sector Manager and the block should be compacted.  

Notes:          The second parameter to CompactBlock() handles the rare situation when
                marking an existing MAPPED sector as DIRTY fails.  In this situation, the
                block needs to be compacted and this sector needs to be treated as DIRTY
                regardless of what the mapping information says...

Returns:        Boolean indicating success.
------------------------------------------------------------------------------------*/
BOOL FileSysFal::HandleWriteFailure(SECTOR_ADDR physicalSectorAddr)
{
    BLOCK_ID     dwBlockID                    = 0;
    SECTOR_ADDR  firstPhysicalSectorAddr    = 0;

    //----- 1. Determine the dwBlockID for this physical sector address -----
    dwBlockID = m_pSectorMgr->GetBlockFromSector (physicalSectorAddr);
                
    //----- 2. Determine first physical sector address for this block -----
    firstPhysicalSectorAddr = m_pSectorMgr->GetStartSectorInBlock (dwBlockID);

    //----- 3. Inform the Sector Manager that any FREE sectors in this block should be removed... -----
    if(!m_pSectorMgr->UnmarkSectorsAsFree(firstPhysicalSectorAddr, m_pRegion->dwSectorsPerBlock))
    {
        ReportError((TEXT("FLASHDRV.DLL:HandleWriteFailure() - m_pSectorMgr->UnmarkSectorsAsFree(%d, %d) failed!\r\n"), firstPhysicalSectorAddr, m_pRegion->dwSectorsPerBlock));
        // NOTE: If this call FAILS, keep on going because CompactBlock() may be still be able to FREE some sectors in order for
        //       for the WRITE to complete.
    }

    //----- 4. Mark the failed sector as dirty, since CompactBlock will unmark it as dirty
    //
    if(!m_pSectorMgr->MarkSectorsAsDirty(physicalSectorAddr, 1))
    {
        ReportError((TEXT("FLASHDRV.DLL:HandleWriteFailure() - Unable to mark physical sector 0x%08x as DIRTY\r\n"), physicalSectorAddr));
    }    

    //----- 5. Compact the block.  Any MAPPED sectors are relocated to another portion of the media -----
    //         and any DIRTY sectors are recycled into FREE sectors.
    if(m_pCompactor->CompactBlock(dwBlockID, physicalSectorAddr, TRUE) == BLOCK_COMPACTION_ERROR)
    {
        ReportError((TEXT("FLASHDRV.DLL:HandleWriteFailure() - CompactBlock(%d, 0x%08x) failed!\r\n"), dwBlockID, physicalSectorAddr));
        goto HANDLE_WRITE_FAILURE_ERROR;
    }

    return TRUE;

HANDLE_WRITE_FAILURE_ERROR:
    return FALSE;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       DeleteSectors()

Description:    Deletes the specified range of logical sectors.

Notes:          To "delete" a logical sector, the appropriate physical
                sector simply needs to be marked as DIRTY.

Returns:        Boolean indicating success.
-------------------------------------------------------------------*/
BOOL FileSysFal::DeleteSectors(DWORD dwStartLogSector, DWORD dwNumSectors)
{
    DWORD i = 0;    
    DWORD dwPhysSector, dwExistingSect;
    SectExInfo sectExInfo;

    if (m_fReadOnly) 
    {
        SetLastError(ERROR_WRITE_PROTECT);
        return FALSE;
    }

    //----- 1. Check to make sure this deletion request doesn't run off the end of the media! -----
    if((dwStartLogSector+dwNumSectors) > g_dwAvailableSectors)
    {
        ReportError((TEXT("FLASHDRV.DLL:DeleteSectors() - Delete sectors (0x%08x - 0x%08x) exceeds the media size!\r\n"),
                                    dwStartLogSector, dwStartLogSector+dwNumSectors));
        goto DELETE_ERROR;
    }
    
    CELOG_DeleteSectors(dwStartLogSector, dwNumSectors);
    
    //----- 2. Setup the sector mapping info to mark this physical sector as DIRTY -----
    //         NOTE: Since we don't know the contents of the sector mapping info for this physical sector,
    //               we can safely write all '1's (except the bit used to indicate the sector is DIRTY ).
    for(i=0; i<dwNumSectors; i++)
    {

        //----- 3. Lookup the physical sector address for this logical sector -----
        if((!m_pMap->GetPhysicalSectorAddr((SECTOR_ADDR)(dwStartLogSector+i), &dwPhysSector))
			|| (dwPhysSector == UNMAPPED_LOGICAL_SECTOR) )
        {
            DEBUGMSG(ZONE_DELETE, (TEXT("delete logcal sector:0x%08x, dwPhysSector:0x%x not writen\r\n"), dwStartLogSector+i, dwPhysSector));
            // Redundant call to delete sector.  Just ignore it.
            return TRUE;
        }

        // map get not physical sector, is write count, must be deleted sect
        if(!IS_VALID_PHY_SECT(dwPhysSector))
        {
            DEBUGMSG(ZONE_DELETE, (TEXT("delete logcal sector:0x%08x, dwPhysSector:0x%x had been deleted\r\n"), dwStartLogSector+i, dwPhysSector));
            sectExInfo.dw=dwPhysSector;
            ASSERT(0==sectExInfo.del);
            // Redundant call to delete sector.  Just ignore it.
            return TRUE;
        }

        if(!GetWtCnt(dwStartLogSector+i, &sectExInfo, NULL))
        {
            sectExInfo.dw=0xffffffff;
            sectExInfo.iWtCnt=GetMaxWtCnt();
        }
        sectExInfo.del=0;
        m_pWtCnt->CacheWtCnt(dwStartLogSector+i, &sectExInfo);

        //----- 4. Mark the physical sector address as DIRTY -----
        if(!m_pMap->MapLogicalSector(dwStartLogSector+i, sectExInfo.dw, &dwExistingSect))
        {
            goto DELETE_ERROR;
        }

        //----- 6. Inform the Sector Manager that this sector is dirty... -----
        ASSERT(IS_VALID_PHY_SECT(dwExistingSect));
        if(!m_pSectorMgr->MarkSectorsAsDirty(dwExistingSect, 1))
        {
            ReportError((TEXT("FLASHDRV.DLL:DeleteSectors() - FATAL_ERROR: SM_MarkSectorsAsDirty(0x%08x) failed!\r\n"), dwExistingSect));
            goto DELETE_ERROR;
        }

    }

    return TRUE;

DELETE_ERROR:
    return FALSE;
}

VOID Fal::DumpInfos()
{
    
    RETAILMSG(1, (TEXT("FAL info free:0x%x, dirty:0x%x, mapped:0x%x, all block:0x%x, unusable:0x%x\r\n")\
        , m_pSectorMgr->GetNumberOfFreeSectors(), m_pSectorMgr->GetNumDirtySectors(), m_pMap->GetMappedNumb(), m_pRegion->dwNumLogicalBlocks, m_pSectorMgr->GetNumUnusableBlocks()));

    if((m_pSectorMgr->GetNumberOfFreeSectors()+m_pSectorMgr->GetNumDirtySectors()+m_pMap->GetMappedNumb())!=((m_pRegion->dwNumPhysBlocks-m_pSectorMgr->GetNumUnusableBlocks())*m_pRegion->dwSectorsPerBlock))
    {
        
        RETAILMSG(1,(TEXT("!!!sectors check not correct!!!\r\n")));
        ASSERT(0);
    }
}
BOOL FileSysFal::SecureWipe()
{
    DWORD i = 0;
    DWORD dwWipeFlagBlock = 0;

    DEBUGMSG(ZONE_FUNCTION, (TEXT("FLASHDRV.DLL:SecureWipe() - Enter\r\n")));    

    // ----- 1. Mark the first good block with secure in progress
    dwWipeFlagBlock = SetSecureWipeFlag (m_pRegion->dwStartPhysBlock);
    if (dwWipeFlagBlock == INVALID_BLOCK_ID)
    {
        return FALSE;
    }

    // ----- 2. Erase all of the valid blocks on the FLASH region, except 
    // -----    except the first, which contains the secure in progress bit
    for(i = 0; i < m_pRegion->dwStartPhysBlock + m_pRegion->dwNumPhysBlocks; i++)
    {
        if(i==dwWipeFlagBlock)
        {
            continue;
        }
        
        if(FMD.pGetBlockStatus((BLOCK_ID)i) & (BLOCK_STATUS_BAD | BLOCK_STATUS_RESERVED))                     
        {
            // Don't erase bad blocks on the media or blocks reserved for other use (such as bootloader).
            continue;
        }
            
        if(!FMD.pEraseBlock((BLOCK_ID)i))
        {
                
            if(!FMD.pSetBlockStatus((BLOCK_ID)i, BLOCK_STATUS_BAD))
            {
                ReportError((TEXT("FLASHDRV.SecureWipe() - FMD_MarkBlockBad(%d) failed\r\n"), i));
                return FALSE;
            }
            m_pSectorMgr->MarkBlockUnusable(i);
        }
    }


    // ----- 4. Erase the first good block
    if(!FMD.pEraseBlock((BLOCK_ID)dwWipeFlagBlock))
    {
        if(!FMD.pSetBlockStatus((BLOCK_ID)dwWipeFlagBlock, BLOCK_STATUS_BAD))
        {
            ReportError((TEXT("FLASHDRV.SecureWipe() - FMD_MarkBlockBad(%d) failed\r\n"), dwWipeFlagBlock));
        }
        m_pSectorMgr->MarkBlockUnusable(dwWipeFlagBlock);
    }

    return TRUE;
}


DWORD FileSysFal::SetSecureWipeFlag (DWORD dwStartBlock)
{
    DWORD i = 0;
    SECTOR_ADDR physicalSector;
    SectorMappingInfo sectorMappingInfo;

    if (m_fReadOnly) 
    {
        SetLastError(ERROR_WRITE_PROTECT);
        return INVALID_BLOCK_ID;
    }
    
    if(!m_pSectorMgr->GetNextFreeSector(&physicalSector, FALSE))
    {
        return INVALID_BLOCK_ID;
    }

    memset(&sectorMappingInfo, 0xff, sizeof(sectorMappingInfo));
    MarkSecureWipeInProgress(sectorMappingInfo);

    if(!FMD.pWriteSector(physicalSector, NULL, (PSectorInfo)&sectorMappingInfo, 1))
    {   
        ReportError((TEXT("FLASHDRV.DLL:FileSysFal::SecureWipe() - Unable to mark sector in progress!\r\n")));
        return INVALID_BLOCK_ID;
    }

    return m_pSectorMgr->GetBlockFromSector(physicalSector);
}

DWORD Fal::GetValidBlockNumb()
{
    return m_pRegion->dwNumPhysBlocks-m_pSectorMgr->GetNumUnusableBlocks()-m_pRegion->dwCompactBlocks;
}

BOOL Fal::GetBlockSpareInfos(DWORD dwBlockID, LPBYTE pSectorBuff, SectorInfo* psi, DWORD dwStopOnStatus, DWORD* pdwStatus, BOOL* pfUncaped)
{
    BOOL fRet=FALSE;
	SectorInfo spareInfo[2];
	if(NULL==psi)
	{
		psi=spareInfo;
	}

    if(pdwStatus)
    {
        *pdwStatus=0;
    }

    memset(pSectorBuff, 0xff, m_pRegion->dwBytesPerBlock/m_pRegion->dwSectorsPerBlock);

    // get cap signature first
	if(FMDHOOK_ReadCapSector(dwBlockID, pSectorBuff, psi))
	{
        if(CAP_SECT_SIG==psi[0].dwReserved1)
        {
        // caped 
            *pfUncaped=FALSE;
            fRet=TRUE;

            if(pdwStatus)
            {
                *pdwStatus=FMDHOOKInternal_GetBlockStatus(dwBlockID, (SectorInfo*)pSectorBuff);
            }

            return TRUE;
        }
	}
    else
    {
        // try get status if this block has block id related status
        DWORD dwStatus=FMDHOOKInternal_GetBlockStatus(dwBlockID, NULL);
        if(pdwStatus)
        {
            *pdwStatus=dwStatus;
        }

        if(dwStopOnStatus&dwStatus)
        {
            return TRUE;
        }
    }

// not caped or read cap err,  try read by sector!
    *pfUncaped=TRUE;
    SECTOR_ADDR sectorAddr=m_pSectorMgr->GetStartSectorInBlock(dwBlockID);
    SectorMappingInfo* pSectMapInfo=(SectorMappingInfo*)pSectorBuff;

    DWORD i;
    fRet=TRUE;
    DWORD dwStatus=0;
	for(i=0; i<m_pRegion->dwSectorsPerBlock; i++, pSectMapInfo++)
	{
		if(!FMD.pReadSector(sectorAddr+i, NULL, (SectorInfo*)pSectMapInfo, 1))
		{
            if(0==i)
            {
                // try get status if this block has block id related status
                dwStatus=FMDHOOKInternal_GetBlockStatus(dwBlockID, NULL);
                if(pdwStatus)
                {
                    *pdwStatus=dwStatus;
                }
        
                if(dwStopOnStatus&dwStatus)
                {
                    return TRUE;
                }
            }
            if(fRet)
            {
                dwStatus|=FMD.pGetBlockStatus(dwBlockID);
                if(pdwStatus)
                {
                    *pdwStatus=dwStatus;
                }
                if(dwStopOnStatus&dwStatus)
                {
                    return TRUE;
                }
            }

            // mark as read error
            DEBUGMSG(1,(TEXT("!!! read sector:0x%x spare info err!!!\r\n"), sectorAddr+i));
            memset(pSectMapInfo, 0xff, sizeof(SectorInfo));
            pSectMapInfo->fDataStatus &= (~SECTOR_WRITE_IN_PROGRESS);   // read as write in process, this will add to dirty list
            // try read all even read error!!!
            fRet=FALSE;
			continue;
		}

        if(0==i)
        {
            dwStatus=FMDHOOKInternal_GetBlockStatus(dwBlockID, (SectorInfo*)pSectMapInfo);
            // stop read other sectors if this block has the special status
            if(pdwStatus)
            {
                *pdwStatus=dwStatus;
            }

            if(dwStopOnStatus & dwStatus)
            {
                break;
            }
        }

        // free sector should continued to block end but read anyway
        if(IsSectorFree((*pSectMapInfo)))
        {
			DEBUGMSG((ZONE_WARNING)&&(i!=0),(TEXT("!!find free sector:0x%x no in block begin!!\r\n"), sectorAddr+i));
            break;
        }
	}

    if((m_pRegion->dwSectorsPerBlock==i) && (fRet))
    {
        DEBUGMSG(1,(TEXT("!!!all sector in block:0x%x is writed but not caped, try cap it!!!\r\n"), dwBlockID));
        SectorMappingInfo mapInfo;
        memset(&mapInfo, 0xff, sizeof(mapInfo));
        mapInfo.logicalSectorAddr=CAP_SECT_SIG;
        if(!FMDHOOK_WriteCapSector(dwBlockID, pSectorBuff, (PSectorInfo)&mapInfo))
        {
            DEBUGMSG(1,(TEXT("!!!GetBlockSpareInfos block:0x%x try cap it faild!!!\r\n"), dwBlockID));
            fRet=FALSE;
        }
        else
        {
            *pfUncaped=FALSE;
        }
    }

	return fRet;
}

BOOL Fal::ScanSectWriteCnt(SECTOR_ADDR logicalSectorAddr, SectExInfo* pSectExInfo)
{
    SectorMappingInfo*  psectorMappingInfo=(SectorMappingInfo*)LocalAlloc(LPTR, m_pRegion->dwBytesPerBlock/m_pRegion->dwSectorsPerBlock);
    if(!psectorMappingInfo)
    {
        return FALSE;
    }

    pSectExInfo->dw=0xffffffff;

    for (DWORD dwBlockID=m_pRegion->dwStartPhysBlock
        ; dwBlockID<m_pRegion->dwStartPhysBlock+m_pRegion->dwNumPhysBlocks
        ; dwBlockID++ )
    {
        DWORD dwStatus;
		if(!GetBlockSpareInfos(dwBlockID, (LPBYTE)psectorMappingInfo, NULL, (BLOCK_STATUS_RESERVED | BLOCK_STATUS_BAD), &dwStatus, NULL))
		{
            continue;
		}
        
        if (dwStatus & (BLOCK_STATUS_RESERVED | BLOCK_STATUS_BAD))
        {
            continue;
        }

        if (dwStatus & BLOCK_STATUS_READONLY)
        {   
            if( logicalSectorAddr>=psectorMappingInfo[0].logicalSectorAddr
                && logicalSectorAddr<(psectorMappingInfo[0].logicalSectorAddr+m_pRegion->dwSectorsPerBlock) )
            {
                // read only write count will not changed
                if(pSectExInfo->iWtCnt<psectorMappingInfo[0].exInfo.iWtCnt)
                {
                    pSectExInfo->dw=psectorMappingInfo[0].exInfo.dw;
                }
            }
        }

        
        for(DWORD i=0; i<m_pRegion->dwSectorsPerBlock; i++)
        {
            if(logicalSectorAddr==psectorMappingInfo[i].logicalSectorAddr)
            {
                if(pSectExInfo->iWtCnt<psectorMappingInfo[i].exInfo.iWtCnt)
                {
                    pSectExInfo->dw=psectorMappingInfo[i].exInfo.dw;
                }
            }
        }
    }

    LocalFree(psectorMappingInfo);
    
    return TRUE;
   
}

BOOL FileSysFal::HandleReadFailure(SECTOR_ADDR physicalSectorAddr)
{
    BLOCK_ID     dwBlockID                    = 0;
    SECTOR_ADDR  firstPhysicalSectorAddr    = 0;

    RETAILMSG(ZONE_ERROR||ZONE_WARNING,(TEXT("HandleReadFailure physicalSectorAddr:0x%x!\r\n"), physicalSectorAddr));

    //----- 1. Determine the dwBlockID for this physical sector address -----
    dwBlockID = m_pSectorMgr->GetBlockFromSector (physicalSectorAddr);
                
    //----- 2. Determine first physical sector address for this block -----
    firstPhysicalSectorAddr = m_pSectorMgr->GetStartSectorInBlock (dwBlockID);

    //----- 3. Inform the Sector Manager that any FREE sectors in this block should be removed... -----
    if(!m_pSectorMgr->UnmarkSectorsAsFree(firstPhysicalSectorAddr, m_pRegion->dwSectorsPerBlock))
    {
        ReportError((TEXT("FLASHDRV.DLL:HandleReadFailure() - m_pSectorMgr->UnmarkSectorsAsFree(%d, %d) failed!\r\n"), firstPhysicalSectorAddr, m_pRegion->dwSectorsPerBlock));
        // NOTE: If this call FAILS, keep on going because CompactBlock() may be still be able to FREE some sectors in order for
        //       for the WRITE to complete.
    }

    //----- 5. Compact the block.  Any MAPPED sectors are relocated to another portion of the media -----
    //         and any DIRTY sectors are recycled into FREE sectors.
    if(m_pCompactor->CompactBlock(dwBlockID, USE_SECTOR_MAPPING_INFO) == BLOCK_COMPACTION_ERROR)
    {
        ReportError((TEXT("FLASHDRV.DLL:HandleReadFailure() - CompactBlock(%d, 0x%08x) failed!\r\n"), dwBlockID, physicalSectorAddr));
        goto HANDLE_WRITE_FAILURE_ERROR;
    }

    return TRUE;

HANDLE_WRITE_FAILURE_ERROR:
    return FALSE;
}

BOOL FileSysFal::GetWtCnt(SECTOR_ADDR sectorAddr, SectExInfo* pSectExInfo, BOOL* pfCached)
{
	if(pfCached)
	{
		*pfCached=FALSE;
	}

	// try get on cache first 
	if(m_pWtCnt->GetWtCnt(sectorAddr, pSectExInfo))
	{
        if(pfCached)
        {
            *pfCached=TRUE;
        }
	}
    else
    {
        //
        // to find on flash
        SECTOR_ADDR dwPhysSector;
        if(m_pMap->GetPhysicalSectorAddr(sectorAddr, &dwPhysSector) 
            && (dwPhysSector != UNMAPPED_LOGICAL_SECTOR))
        {
            if(IS_VALID_PHY_SECT(dwPhysSector))
            {
                if(!ReadWtCntOnFlash(dwPhysSector, pSectExInfo))
                {
                    // can't read from flash
                    return FALSE;
                }
            }
            // it is a SectExInfo
            else
            {
                pSectExInfo->dw=dwPhysSector;
            }
        }
        // no info about exinfo, so give it a init val
        else
        {
            pSectExInfo->dw=0xffffffff;
        }
    }

    if(MAX_WRITE_CNT==pSectExInfo->iWtCnt)
    {
        ReportError((TEXT("write count too large, please format this flash!!!!\r\n")));
        return FALSE;
    }

	return TRUE;
}


BOOL FileSysFal::ReadWtCntOnFlash(SECTOR_ADDR sectorAddr, SectExInfo* pSectExInfo)
{
    SectorMappingInfo mappingInfo;

    if(!FMD.pReadSector(sectorAddr, NULL, (SectorInfo*)&mappingInfo, 1))
    {
        RETAILMSG( 1, (TEXT("!!!ReadWtCntOnFlash sector:0x%x faild!!!\r\n"), sectorAddr) );              
        return FALSE;
    }

    *pSectExInfo=mappingInfo.exInfo;

    return TRUE;
}


